#include "documentmanager.h"

#include "../documents/swpartifactdocument.h"
#include "../documents/swpprocessdocument.h"
#include "../documents/swpprojectdocument.h"
#include "../documents/swpresourcedocument.h"
#include "../documents/swpsimulationcasedocument.h"
#include "../mainwindow.h"
#include "../swpcommon.h"
#include "../utils/undomanager.h"

#include <coremimetypes.h>
#include <ieditor.h>
#include <ieditorarea.h>
#include <inputdialog.h>
#include <ipersistenceassistant.h>
#include <ipersistentdocument.h>
#include <iperspective.h>
#include <istorage.h>
#include <linkseditordialog.h>
#include <mimetypemanager.h>
#include <persistence.h>
#include <validatormanager.h>

#include <QtCore/QUrl>
#include <QtGui/QMessageBox>
#include <QtGui/QValidator>

using namespace core;
using namespace gui;
using namespace swp;

typedef QSharedPointer<IDocument> Document;

DocumentManager* documentManager = 0;

DocumentManager* DocumentManager::getInstance()
{
    if (documentManager == 0)
    {
        documentManager = new DocumentManager();
    }

    return documentManager;
}

void DocumentManager::destroy()
{
    delete documentManager;
    documentManager = 0;
}

QList<QSharedPointer<IContainerDocument> > DocumentManager::openedProjects()
    const
{
    QList<QSharedPointer<IContainerDocument> > ret;

    foreach (const Document& document, m_openedDocuments)
    {
        if (document->isContainer())
        {
            ret.append(qSharedPointerDynamicCast<IContainerDocument>(
                document));
        }
    }

    return ret;
}

QList<QSharedPointer<IDocument> > DocumentManager::openedDocuments() const
{
    return m_openedDocuments;
}

bool DocumentManager::isOpenedToMemory(
    const QSharedPointer<IDocument>& document) const
{
    return m_openedDocumentsInMemory.contains(document);
}

DocumentManager::DocumentManager() : QObject(), ISessionProvider(),
    m_mimeTypeToNameMapping(),
    m_openedDocuments(),
    m_openedDocumentsInMemory()
{
    MimeTypeManager* mmtm =  MimeTypeManager::getInstance();
    m_mimeTypeToNameMapping[mmtm->mimeType(ACTIVITY_DIAGRAM_MIMETYPE).name()]
        = tr("Activity diagram");
    m_mimeTypeToNameMapping[mmtm->mimeType(CLASS_DIAGRAM_MIMETYPE).name()] =
        tr("Class diagram");
    m_mimeTypeToNameMapping[mmtm->mimeType(SIM_CASE_MIMETYPE).name()] =
        tr("Simulation case");
}

DocumentManager::~DocumentManager()
{

}

QSharedPointer<IPersistenceAssistant> DocumentManager::getNativeAssistant(
    const QSharedPointer<IDocument>& document) const
{
    QList<QSharedPointer<IPersistenceAssistant> > assistants =
        Persistence::getNativeAssistants(document);

    QSharedPointer<IPersistenceAssistant> assistant;

    foreach (const QSharedPointer<IPersistenceAssistant>& _assistant,
        assistants)
    {
        if (!_assistant->getPersistentDocument().isNull())
        {
            assistant = _assistant;
            break;
        }
        else
        {
            Persistence::closeAssistant(_assistant);
        }
    }

    return assistant;
}

DocumentManager::DialogAction DocumentManager::askForSave(
    const QSharedPointer<IDocument>& document) const
{
    DialogAction action = Cancel;

    QMessageBox::StandardButton button = QMessageBox::question(
        qobject_cast<QWidget*>(parent()),
        tr("Save %1").arg(document->isContainer()
            ? "project" : "document"),
                tr("Do you want to save \"%1\"?").arg(
                document->name()), QMessageBox::Yes |
                QMessageBox::No | QMessageBox::Cancel);

    action = button == QMessageBox::Yes ? Yes :
        (button == QMessageBox::No ? No : Cancel);

    return action;
}

DocumentManager::DialogAction DocumentManager::askForRemove(
    const QSharedPointer<IDocument>& document) const
{
    DialogAction action = Cancel;

    QMessageBox::StandardButton button = QMessageBox::question(
        qobject_cast<QWidget*>(parent()),
        tr("Remove document"), tr("Do you want to remove \"%1\"?").arg(
            document->name()), QMessageBox::Yes |
            QMessageBox::No);

    action = button == QMessageBox::Yes ? Yes :
        (button == QMessageBox::No ? No : Cancel);

    return action;
}

DocumentManager::DialogAction DocumentManager::askForDelete(
    const QSharedPointer<IDocument>& document) const
{
    DialogAction action = Cancel;

    const bool isContainer = document->isContainer();

    QMessageBox::StandardButton button = QMessageBox::question(
        qobject_cast<QWidget*>(parent()),
        tr("Delete %1").arg(isContainer
            ? "project" : "document"),
                tr("Do you want to delete \"%1\"%2?").arg(
                    document->name()).arg(
                    isContainer ? " and all its children" : ""),
                    QMessageBox::Yes | QMessageBox::No);

    action = button == QMessageBox::Yes ? Yes :
        (button == QMessageBox::No ? No : Cancel);

    return action;
}

DocumentManager::DialogAction DocumentManager::askForSaveLinks(
    const QSharedPointer<core::IDocument>& document, int linksCount) const
{
    DialogAction action = Cancel;

    const bool isContainer = document->isContainer();

    QMessageBox::StandardButton button = QMessageBox::question(
        qobject_cast<QWidget*>(parent()),
        tr("Save %1's links").arg(isContainer
            ? "project" : "document"),
                tr("Do you want to save \"%1\" links (%2)?\n"
                   "Otherwise the linkage will be lost when you "
                   "close the %3.").arg(
                    document->name()).arg(linksCount).arg(
                        isContainer ? "project" : "document"),
                    QMessageBox::Yes | QMessageBox::No);

    action = button == QMessageBox::Yes ? Yes :
        (button == QMessageBox::No ? No : Cancel);

    return action;
}

QString DocumentManager::getStorage(const QString& title, bool& ok) const
{
    QHash<QString, QString> storages =
        Persistence::registeredStorages();

    QString storageName;
    /*if (storages.size() > 1)
    {*/
        storageName = InputDialog::getTextFromConstraints(
            qobject_cast<QWidget*>(parent()), title,
            tr("Choose storage:"), storages.keys(),
            &ok);
    /*}
    else
    {
        storageName = storages.keys().first();
        ok = true;
    }*/

    return storages[storageName];
}

bool DocumentManager::saveChildDocuments(
    const QSharedPointer<IContainerDocument>& project,
    const QString& storage, bool& canceled)
{
    bool ret = true;
    canceled = false;

    QList<QSharedPointer<IDocument> > toBeRemoved;

    foreach (const QString& category, project->categories())
    {
        const QList<QSharedPointer<IDocument> > documents =
            project->documents(category);

        if (canceled)
        {
            break;
        }

        foreach (const QSharedPointer<IDocument>& document, documents)
        {
            if (document->isDirty())
            {
                QSharedPointer<IPersistenceAssistant> assistant =
                    getNativeAssistant(document);

                const DialogAction action = askForSave(document);
                if (action == Yes)
                {
                    if (assistant.isNull())
                    {
                        QSharedPointer<IPersistentDocument> perDocument =
                            Persistence::save(document, storage, canceled);

                        if (!canceled)
                        {
                            if (perDocument.isNull())
                            {
                                project->removeDocument(category, document);
                                document->setContainer(
                                    QSharedPointer<IContainerDocument>(0));

                                ret = false;
                            }
                            else
                            {
                                document->setDirty(false);

                                emit documentSaved(document);
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        QSharedPointer<IPersistentDocument> perDocument =
                            assistant->save();

                        if (perDocument.isNull())
                        {
                            project->removeDocument(category, document);
                            document->setContainer(
                                QSharedPointer<IContainerDocument>(0));

                            ret = false;
                        }
                        else
                        {
                            document->setDirty(false);

                            emit documentSaved(document);
                        }
                    }
                }
                else if (action == No)
                {
                    toBeRemoved.append(document);

                    ret = false;
                }
                else
                {
                    canceled = true;
                    break;
                }
            }
        }
    }

    if (!canceled)
    {
        foreach (const Document& document, toBeRemoved)
        {
            project->removeDocument(document);
            document->setContainer(
                QSharedPointer<IContainerDocument>(0));

            emit documentRemoved(document);
        }
    }

    return ret;
}

void DocumentManager::deleteChildDocuments(
    const QSharedPointer<core::IContainerDocument>& project)
{
    foreach (const QString& category, project->categories())
    {
        const QList<QSharedPointer<IDocument> > documents =
            project->documents(category);

        foreach (const QSharedPointer<IDocument>& document, documents)
        {
            const QSharedPointer<IPersistenceAssistant>& assistant =
                getNativeAssistant(document);

            if (!assistant.isNull())
            {
                QSharedPointer<IPersistentDocument> perDocument =
                    assistant->getPersistentDocument();

                IEditorArea* editorArea = MainWindow::getInstance()->
                    getCurrentPerspective()->getEditorArea();
                QList<QSharedPointer<IEditor> > editors =
                    editorArea->openedEditors(document);

                foreach (const QSharedPointer<IEditor>& editor, editors)
                {
                    editorArea->confirmClose(editor);
                }

                Persistence::deleteDocument(perDocument);
            }
            else
            {
                document->setContainer(QSharedPointer<IContainerDocument>(0));
            }

            emit documentRemoved(document);
        }
    }
}

void DocumentManager::removeChildDocuments(
    const QSharedPointer<core::IContainerDocument>& project)
{
    foreach (const QString& category, project->categories())
    {
        const QList<QSharedPointer<IDocument> > documents =
            project->documents(category);

        foreach (const QSharedPointer<IDocument>& document, documents)
        {
            document->setContainer(QSharedPointer<IContainerDocument>(0));

            emit documentRemoved(document);
        }
    }
}

QString DocumentManager::getCategory(
    const QSharedPointer<core::IContainerDocument>& project,
    const core::MimeType& documentMimeType) const
{
    QString ret;

    foreach (const QString& categoryName, project->categories())
    {
        if (project->categoryMimeTypes(categoryName).contains(
            documentMimeType))
        {
            ret = categoryName;
            break;
        }
    }

    return ret;
}

void DocumentManager::closeAllLiveAssistans(
    const QSharedPointer<core::IContainerDocument>& project) const
{
    foreach (const QString& category, project->categories())
    {
        const QList<QSharedPointer<IDocument> > documents =
            project->documents(category);

        foreach (const QSharedPointer<IDocument>& document, documents)
        {
            Persistence::closeAssistants(document);
        }
    }
}

void DocumentManager::closeAllOpenedEditors(
    const QSharedPointer<core::IContainerDocument>& project)
{
    foreach (const QString& category, project->categories())
    {
        const QList<QSharedPointer<IDocument> > documents =
            project->documents(category);

        foreach (const QSharedPointer<IDocument>& document, documents)
        {
            const QList<IPerspective* > perspectives =
                MainWindow::getInstance()->getPerspectives();

            foreach (IPerspective* perspetive, perspectives)
            {
                IEditorArea* editorArea = perspetive->getEditorArea();

                if (editorArea != 0)
                {
                    QList<QSharedPointer<IEditor> > editors =
                        editorArea->openedEditors(document);

                    foreach (const QSharedPointer<IEditor>& editor, editors)
                    {
                        editorArea->confirmClose(editor);
                    }
                }
            }

            removeFromOpened(document);
            emit documentClosed(document);
        }
    }
}

void DocumentManager::removeFromOpened(
    const QSharedPointer<IDocument>& document)
{
    if (m_openedDocuments.contains(document))
    {
        m_openedDocuments.removeAll(document);
        UndoManager::getInstance()->destroyStack(document->data());
    }

    if (m_openedDocumentsInMemory.contains(document))
    {
        m_openedDocumentsInMemory.removeAll(document);
    }
}

QString DocumentManager::camelCase(const QString& text) const
{
    QString ret;

    const QStringList list = text.split(" ", QString::SkipEmptyParts);

    foreach (const QString& s, list)
    {
        ret.append(QString("%1%2").arg(s.left(1).toUpper()).arg(
            s.right(s.length() - 1).toLower()));
    }

    return ret;
}

bool DocumentManager::subSave(const QSharedPointer<core::IDocument>&
    document)
{
    bool ret = false;

    QSharedPointer<IPersistenceAssistant> assistant =
        getNativeAssistant(document);

    if (assistant.isNull())
    {
        bool ok;

        const QString title = QString("Save \"%1\" %2").arg(
                document->name()).arg(
            document->isContainer() ? "project" : "document");

        QString storageName;

        const QSharedPointer<IPersistenceAssistant> projAssistant =
            document->isContainer() ? QSharedPointer<IPersistenceAssistant>(0)
            : getNativeAssistant(document->container());

        if (!projAssistant.isNull())
        {
            storageName = projAssistant->forStorage();
            ok = true;
        }
        else
        {
            storageName = getStorage(title, ok);
        }

        if (ok)
        {
            bool canceled = false;

            if (document->isContainer())
            {
                QSharedPointer<IContainerDocument> _document =
                    qSharedPointerDynamicCast<IContainerDocument>(
                        document);

                saveChildDocuments(_document, storageName, canceled);
            }

            if (!canceled)
            {
                const QSharedPointer<IPersistentDocument>& perDocument =
                    Persistence::save(document, storageName, canceled);

                if (!perDocument.isNull() && !canceled)
                {
                    document->setDirty(false);
                    emit documentSaved(document);
                }
            }

            ret = canceled;
        }
    }
    else
    {
        bool canceled = false;

        if (document->isContainer())
        {
            QSharedPointer<IContainerDocument> _document =
                qSharedPointerDynamicCast<IContainerDocument>(
                    document);

            saveChildDocuments(_document, assistant->forStorage(),
                canceled);
        }

        if (!canceled)
        {
            const QSharedPointer<IPersistentDocument>& perDocument =
                assistant->save();

            if (!perDocument.isNull())
            {
                document->setDirty(false);
                emit documentSaved(document);
            }
        }

        ret = canceled;
    }

    return ret;
}

void DocumentManager::openProject(const QUrl& url,
    const QString& storageName)
{
    if (url.isValid() && (!url.authority().isEmpty() ||
        !url.path().isEmpty()))
    {
        QSharedPointer<IPersistentDocument> perDocument =
            Persistence::createDocument(url, storageName);

        if (!perDocument.isNull())
        {
            QSharedPointer<IDocument> document =
                Persistence::open(perDocument);

            if (!m_openedDocuments.contains(document) && !document.isNull())
            {
                m_openedDocuments.append(document);

                emit projectOpened(document);
            }
        }
    }
}

QList<QSharedPointer<IDocument> > DocumentManager::getLinks(
    const QSharedPointer<IContainerDocument>& project,
    const QSharedPointer<ILinkedDocument>& document,
    RequireLinks type, bool reqValidity, bool& ok) const
{
    QList<QSharedPointer<IDocument> > ret;

    QList<QSharedPointer<IDocument> > canBeAdded;

    foreach (const QString& category, project->categories())
    {
        const QList<MimeType> cMimeTypes = project->categoryMimeTypes(
            category);
        const QList<MimeType> linkTypes = document->linksTo();

        foreach (const MimeType& mimeType, linkTypes)
        {
            if (cMimeTypes.contains(mimeType))
            {
                const QList<QSharedPointer<IDocument> > documents =
                        project->documents(category);

                foreach (const Document& candidate, documents)
                {
                    if (linkTypes.contains(candidate->mimeType()) &&
                        (reqValidity ? candidate->isValid() : true))
                    {
                        canBeAdded.append(candidate);
                    }
                }
            }
        }
    }

    LinksEditorDialog dialog(canBeAdded,
        QList<QSharedPointer<IDocument> >(), MainWindow::getInstance(),
        type == One);
    const int result = dialog.exec();

    if (result == QDialog::Accepted)
    {
        ret = dialog.added();
        ok = true;
    }
    else
    {
        ok = false;
    }

    return ret;
}

// public slots
void DocumentManager::newProject()
{
    bool ok;
    const QString text = InputDialog::getText(qobject_cast<QWidget*>(
        parent()), tr("Project name"),
        tr("Enter project name:"), "",
        ValidatorManager::getInstance()->getValidator(
            DOCUMENT_NAME_VALIDATOR),
            QLineEdit::Normal, tr("Project"), &ok);

    if (ok && !text.isEmpty())
    {
        SwpProjectDocument* projectDocument = new SwpProjectDocument(text,
            MimeTypeManager::getInstance()->mimeType(PROJECT_MIMETYPE));
        projectDocument->setDirty();

        QSharedPointer<IDocument> _projectDocument(projectDocument);

        emit projectCreated(_projectDocument);

        m_openedDocuments.append(_projectDocument);
    }
}

void DocumentManager::openProject()
{
    openProject("", "");
}

void DocumentManager::openProject(const QString& url, const QString& storage)
{
    bool ok;

    if (!url.isEmpty() && !storage.isEmpty())
    {
        QUrl _url = QUrl(url);

        openProject(_url, storage);
    }
    else {
        const QString storageName = getStorage(tr("Open project"), ok);

        if (ok)
        {
            IStorage* storage = Persistence::getStorage(storageName);

            if (storage != 0)
            {
                QUrl _url = storage->urlForOpen(
                    MimeTypeManager::getInstance()->mimeType(
                        PROJECT_MIMETYPE));

                openProject(_url, storageName);
            }
        }
    }
}

void DocumentManager::addDocument(
    const QSharedPointer<core::IDocument>& project,
    const QList<core::MimeType>& possibilites, uint requireLinks,
    bool reqValidity)
{
    Q_ASSERT(!project.isNull() && project->isContainer());

    const QSharedPointer<IContainerDocument>& _project =
        qSharedPointerDynamicCast<IContainerDocument>(project);

    QList<MimeType> allMimeTypes;

    if (possibilites.isEmpty())
    {
        foreach (const QString& categoryName, _project->categories())
        {
            allMimeTypes.append(_project->categoryMimeTypes(categoryName));
        }
    }
    else
    {
        allMimeTypes.append(possibilites);
    }

    QHash<QString, MimeType> commentToMimeTypeMapping;

    foreach (const MimeType& mimeType, allMimeTypes)
    {
        commentToMimeTypeMapping[mimeType.comment()] = mimeType;
    }

    bool ok;
    QString documentType;

    if (allMimeTypes.size() > 1)
    {
        documentType = InputDialog::getTextFromConstraints(
            qobject_cast<QWidget*>(parent()), tr("Document type"),
            tr("Choose document type:"), commentToMimeTypeMapping.keys(),
            &ok);
    }
    else
    {
        documentType = allMimeTypes.first().comment();
        ok = true;
    }

    if (ok)
    {
        const MimeType chosenDocumentType = commentToMimeTypeMapping[
            documentType];

        const QString docType = m_mimeTypeToNameMapping[
            chosenDocumentType.name()];

        const QString name = InputDialog::getText(qobject_cast<QWidget*>(
            parent()), tr("Document name"),
            tr("%1 Document name:").arg(docType), "",
            ValidatorManager::getInstance()->getValidator(
                DOCUMENT_NAME_VALIDATOR),
                QLineEdit::Normal, tr("%1Document").arg(
                    camelCase(docType)), &ok);

        if (ok && !name.isEmpty())
        {
            IDocument* document = 0;

            if (chosenDocumentType ==
                MimeTypeManager::getInstance()->mimeType(
                    ACTIVITY_DIAGRAM_MIMETYPE))
            {
                document = new SwpProcessDocument(name,
                    MimeTypeManager::getInstance()->mimeType(
                        ACTIVITY_DIAGRAM_MIMETYPE), _project, false);
            }
            else if (chosenDocumentType ==
                MimeTypeManager::getInstance()->mimeType(
                    CLASS_DIAGRAM_MIMETYPE))
            {
                document = new SwpResourceDocument(name,
                    MimeTypeManager::getInstance()->mimeType(
                        CLASS_DIAGRAM_MIMETYPE), _project, false);
            }
            else if (chosenDocumentType ==
                MimeTypeManager::getInstance()->mimeType(
                    SIM_CASE_MIMETYPE))
            {
                document = new SwpSimulationCaseDocument(name,
                    MimeTypeManager::getInstance()->mimeType(
                        SIM_CASE_MIMETYPE), _project, false);
            }
            else
            {
                // do nothing
            }

            QSharedPointer<IDocument> _document(document);

            RequireLinks reqLinks = static_cast<RequireLinks>(requireLinks);

            if (reqLinks != None && _document->isLinked())
            {
                QSharedPointer<ILinkedDocument> linkedDoc =
                    qSharedPointerDynamicCast<ILinkedDocument>(_document);

                const QList<QSharedPointer<IDocument> > links =
                    getLinks(_project, linkedDoc, reqLinks, reqValidity, ok);

                foreach (const QSharedPointer<IDocument>& link, links)
                {
                    linkedDoc->addLink(link);
                }
            }

            if (ok)
            {
                if (!_document.isNull())
                {
                    const QString category = getCategory(
                        _project, chosenDocumentType);

                    _project->addDocument(category, _document);
                    _document->setContainer(_project);

                    _project->setDirty();
                    _document->setDirty();
                    emit documentAdded(project, _document);

                    openDocument(_document);
                }
            }
        }
    }
}

void DocumentManager::addDocument(const QSharedPointer<IDocument>& project,
    const QSharedPointer<IDocument>& document,
    uint requireLinks, bool reqValidity)
{
    Q_ASSERT(!document.isNull() && !project.isNull() &&
        project->isContainer());

    const QSharedPointer<IContainerDocument>& _project =
        qSharedPointerDynamicCast<IContainerDocument>(project);

    QSharedPointer<IDocument> _document(document);

    RequireLinks reqLinks = static_cast<RequireLinks>(requireLinks);

    bool ok = true;

    if (reqLinks != None && _document->isLinked())
    {
        QSharedPointer<ILinkedDocument> linkedDoc =
            qSharedPointerDynamicCast<ILinkedDocument>(_document);

        const QList<QSharedPointer<IDocument> > links =
            getLinks(_project, linkedDoc, reqLinks, reqValidity, ok);

        foreach (const QSharedPointer<IDocument>& link, links)
        {
            linkedDoc->addLink(link);
        }
    }

    if (ok)
    {
        if (!_document.isNull())
        {
            const QString category = getCategory(
                _project, document->mimeType());

            _project->addDocument(category, _document);
            _document->setContainer(_project);

            _project->setDirty();
            _document->setDirty();
            emit documentAdded(project, _document);

            openDocument(_document);
        }
    }
}

void DocumentManager::removeDocument(
    const QSharedPointer<core::IDocument>& project,
    const QSharedPointer<core::IDocument>& document,
    bool force)
{
    Q_ASSERT(!project.isNull() && project->isContainer());

    const DialogAction action = force ? DocumentManager::Yes :
        askForRemove(document);

    if (action == DocumentManager::Yes)
    {
        const QSharedPointer<IContainerDocument>& _project =
            qSharedPointerDynamicCast<IContainerDocument>(project);

        _project->removeDocument(document);
        document->setContainer(QSharedPointer<IContainerDocument>(0));

        emit documentRemoved(project, document);
    }
}

void DocumentManager::deleteDocument(
    const QSharedPointer<core::IDocument>& document)
{
    const DialogAction action = askForDelete(document);

    if (action == DocumentManager::Yes)
    {
        const QSharedPointer<IPersistenceAssistant>& assistant =
            getNativeAssistant(document);

        if (document->isContainer())
        {
            const QSharedPointer<IContainerDocument>& project =
                qSharedPointerDynamicCast<IContainerDocument>(document);

            Q_ASSERT(!project.isNull()); // shouldn't ever happen

            deleteChildDocuments(project);

            closeAllOpenedEditors(project);
            closeAllLiveAssistans(project);
        }
        else
        {
            const QList<IPerspective* > perspectives =
                MainWindow::getInstance()->getPerspectives();

            foreach (IPerspective* perspetive, perspectives)
            {
                IEditorArea* editorArea = perspetive->getEditorArea();

                if (editorArea != 0)
                {
                    QList<QSharedPointer<IEditor> > editors =
                        editorArea->openedEditors(document);

                    foreach (const QSharedPointer<IEditor>& editor, editors)
                    {
                        editorArea->confirmClose(editor);
                    }
                }
            }

            document->container()->setDirty();
            document->container()->removeDocument(document);
            document->setContainer(QSharedPointer<IContainerDocument>(0));

            Persistence::closeAssistants(document);
        }

        if (!assistant.isNull())
        {
            QSharedPointer<IPersistentDocument> perDocument =
                assistant->getPersistentDocument();

            const bool success = Persistence::deleteDocument(perDocument);

            if (success)
            {
                emit documentRemoved(document);
                removeFromOpened(document);
            }
        }
        else
        {
            emit documentRemoved(document);
            removeFromOpened(document);
        }
    }
}

void DocumentManager::openDocument(
    const QSharedPointer<core::IDocument>& document)
{
    Q_ASSERT(!document.isNull() && !document->isContainer());

    if (document->isFuture())
    {
        QSharedPointer<IPersistenceAssistant> assistant =
            getNativeAssistant(document);

        if (assistant.isNull())
        {
            assistant = Persistence::getNativeAssistant(document,
                Persistence::WILD_CARD);
        }

        Q_ASSERT(!assistant.isNull());

        assistant->open();
    }

    if (m_openedDocumentsInMemory.contains(document))
    {
        m_openedDocumentsInMemory.removeAll(document);
    }

    if (!m_openedDocuments.contains(document))
    {
        m_openedDocuments.append(document);
        UndoManager::getInstance()->createStack(document->data());

        emit documentOpened(document);
    }

    const QList<IPerspective* > perspectives =
        MainWindow::getInstance()->getPerspectives();

    foreach (IPerspective* perspetive, perspectives)
    {
        if (perspetive->canOpen(document->mimeType()))
        {
            perspetive->getEditorArea()->openEditor(document);
        }
    }
}

void DocumentManager::openDocumentToMemory(
    const QSharedPointer<core::IDocument>& document)
{
    Q_ASSERT(!document.isNull() && !document->isContainer());

    if (document->isFuture())
    {
        QSharedPointer<IPersistenceAssistant> assistant =
            getNativeAssistant(document);

        if (assistant.isNull())
        {
            assistant = Persistence::getNativeAssistant(document,
                Persistence::WILD_CARD);
        }

        Q_ASSERT(!assistant.isNull());

        assistant->open();
    }

    if (!m_openedDocuments.contains(document) &&
        !m_openedDocumentsInMemory.contains(document))
    {
        m_openedDocumentsInMemory.append(document);

        emit documentOpened(document);
    }
}

void DocumentManager::saveDocument(
    const QSharedPointer<core::IDocument>& document)
{
    if (document->isDirty())
    {
        subSave(document);
    }
}

void DocumentManager::closeDocument(
    const QSharedPointer<core::IDocument>& document, bool* canceled)
{
    Q_ASSERT(!document.isNull());

    QSharedPointer<IPersistenceAssistant> assistant =
        getNativeAssistant(document);

    bool properClose = true;
    bool isCanceled = false;

    if (document->isDirty())
    {
        const DialogAction action = askForSave(document);
        if (action == Yes)
        {
            if (assistant.isNull())
            {
                bool ok;

                const QString title = QString("Save %1").arg(
                    document->isContainer() ? "project" : "document");

                const QString storageName = getStorage(title, ok);

                if (ok)
                {
                    bool _canceled = false;

                    if (document->isContainer())
                    {
                        QSharedPointer<IContainerDocument> _document =
                            qSharedPointerDynamicCast<IContainerDocument>(
                                document);

                        saveChildDocuments(_document, storageName, _canceled);
                    }

                    isCanceled = isCanceled || _canceled;

                    if (!_canceled)
                    {
                        const QSharedPointer<IPersistentDocument>& perDocument
                            = Persistence::save(document, storageName,
                                _canceled);

                        isCanceled = isCanceled || _canceled;

                        if (!_canceled)
                        {
                            assistant = Persistence::getNativeAssistant(
                                document, storageName);

                            if (!perDocument.isNull())
                            {
                                document->setDirty(false);
                                emit documentSaved(document);
                                emit documentClosed(document);
                                removeFromOpened(document);
                            }
                            else
                            {
                                emit documentClosed(document);
                                removeFromOpened(document);

                                properClose = false;

                                assistant = Persistence::getNativeAssistant(
                                    document, Persistence::WILD_CARD);
                            }
                        }
                    }
                }
                else
                {
                    isCanceled = true;
                }
            }
            else
            {
                bool _canceled = false;

                if (document->isContainer())
                {
                    QSharedPointer<IContainerDocument> _document =
                        qSharedPointerDynamicCast<IContainerDocument>(
                            document);

                    saveChildDocuments(_document, assistant->forStorage(),
                        _canceled);
                }

                isCanceled = isCanceled || _canceled;

                if (!_canceled)
                {
                    const QSharedPointer<IPersistentDocument>& perDocument =
                        assistant->save();

                    if (!perDocument.isNull())
                    {
                        document->setDirty(false);
                        emit documentSaved(document);
                        emit documentClosed(document);
                        removeFromOpened(document);
                    }
                    else
                    {
                        emit documentClosed(document);
                        removeFromOpened(document);

                        properClose = false;

                        assistant = Persistence::getNativeAssistant(document,
                            Persistence::WILD_CARD);
                    }
                }
            }
        }
        else if (action == No)
        {
            emit documentClosed(document);
            removeFromOpened(document);

            properClose = false;

            if (assistant.isNull())
            {
                assistant = Persistence::getNativeAssistant(document,
                    Persistence::WILD_CARD);
            }
        }
        else
        {
            isCanceled = true;
        }
    }
    else
    {
        bool _canceled = false;

        if (document->isContainer())
        {
            QSharedPointer<IContainerDocument> _document =
                qSharedPointerDynamicCast<IContainerDocument>(
                    document);

            if (_document->isChildrenDirty())
            {
                saveChildDocuments(_document, assistant->forStorage(),
                    _canceled);
            }
        }

        isCanceled = isCanceled || _canceled;

        if (!_canceled)
        {
            emit documentClosed(document);
            removeFromOpened(document);
        }
    }

    if (!isCanceled)
    {
        assistant->closeAndKeepFuture();

        const QList<IPerspective* > perspectives =
            MainWindow::getInstance()->getPerspectives();

        foreach (IPerspective* perspetive, perspectives)
        {
            IEditorArea* editorArea = perspetive->getEditorArea();

            if (editorArea != 0)
            {
                QList<QSharedPointer<IEditor> > editors =
                    editorArea->openedEditors(document);

                foreach (const QSharedPointer<IEditor>& editor, editors)
                {
                    editorArea->confirmClose(editor);
                }
            }
        }

        if (document->isContainer())
        {
            QSharedPointer<IContainerDocument> _document =
                qSharedPointerDynamicCast<IContainerDocument>(
                    document);

            if (properClose)
            {
                closeAllOpenedEditors(_document);
                closeAllLiveAssistans(_document);
            }
        }
        else if (!properClose)
        {
            const QSharedPointer<IDocument>& project =
                qSharedPointerDynamicCast<IDocument>(document->container());

            if (!project.isNull() &&
                assistant->getPersistentDocument().isNull())
            {
                removeDocument(project, document, true);
            }
        }

        if (document->container().isNull())
        {
            Persistence::closeAssistants(document);
        }
    }

    if (canceled != 0)
    {
        *canceled = isCanceled;
    }
}

void DocumentManager::closeDocument(
    const QSharedPointer<core::IEditor>& editor)
{
    Q_ASSERT(!editor->document().isNull());

    bool canceled = false;

    closeDocument(editor->document(), &canceled);

    if (!canceled)
    {
        emit documentClosed(editor);
    }
}

void DocumentManager::moveDocument(
    const QSharedPointer<core::IDocument>& project,
    const QSharedPointer<core::IDocument>& document)
{
    Q_ASSERT(!document->container().isNull());

    QSharedPointer<IContainerDocument> container =
        qSharedPointerDynamicCast<IContainerDocument>(document->container());

    container->removeDocument(document);
    container->setDirty();

    if (!project.isNull())
    {
        QSharedPointer<IContainerDocument> _project =
            qSharedPointerDynamicCast<IContainerDocument>(project);

        const QString category = getCategory(_project, document->mimeType());

        _project->addDocument(category, document);
        _project->setDirty();
        document->setContainer(_project);

        emit documentAdded(project, document);
    }
}

void DocumentManager::checkCleanState(
    const QSharedPointer<core::IDocument>& document)
{
    QSharedPointer<IPersistenceAssistant> assistant =
        getNativeAssistant(document);

    if (document->isDirty() && !assistant.isNull())
    {
        document->setDirty(false);
    }
}
